//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <guidseed.h>

_ELASTOS_NAMESPACE_USING

extern const DictWord c_sysDict[c_cMaxDictWords];
extern const DictWord c_inscapeDict[c_cMaxDictWords];

DECL_GUID_ENCODER(RecodeSymbol);
DECL_GUID_ENCODER(StaticDict);

static const GuidEncoder c_encoders[] = {
    GUID_ENCODER_ENTRY(RecodeSymbol, NULL),
    GUID_ENCODER_ENTRY(StaticDict, c_sysDict),
    GUID_ENCODER_ENTRY(StaticDict, c_inscapeDict),
};
static const int c_cEncoders = sizeof(c_encoders) / sizeof(GuidEncoder);

UInt16 GenerateCheckSum(const UInt16 *pData, int nSize)
{
    UInt32 dwSum = 0;
    UInt16 wSize = nSize / sizeof(UInt16);

    while (wSize > 0) {
        dwSum += *pData;
        pData++;
        wSize--;
    }
    if (nSize & 1) dwSum += (UInt16)*(Byte *)pData << 8;

    dwSum = (dwSum >> 16) + (dwSum & 0xffff);
    dwSum += (dwSum >> 16);

    wSize = (UInt16)dwSum & 0xffff;
    return ~wSize;
}

UInt16 GenerateSeedCheckSum(const wchar_t *pwszSeed, int nLength)
{
    char seed[_MAX_SEED_LEN + 1];

    for (int n = 0; n < nLength; n++) {
        seed[n] = (char)pwszSeed[n];
    }
    return GenerateCheckSum((UInt16 *)seed, nLength);
}

UInt16 GenerateGuidCheckSum(REMuid guid)
{
    UInt16 wCheckSum;

    wCheckSum = GenerateCheckSum((UInt16 *)((Byte *)&guid + 1), 15);
    while (0 != (wCheckSum & 0xffe0)) {
        wCheckSum = (wCheckSum >> 5) + (wCheckSum & 0x1f);
    }
    return wCheckSum;
}

ECode _DebugHelper_GuidFromSeedString(const wchar_t *pwszSeed, PEMuid pGuid)
{
    wchar_t wszLongSeed[c_nMaxSeedSize + 1];
    Byte *p;
    UInt32 result[3];
    int n, nEncoder, cEncoded, cPrevEncoded;

    n = wcslen(pwszSeed);
    if (n > c_nMaxSeedSize) {
        memcpy(wszLongSeed, pwszSeed, c_nMaxSeedSize * 2);
        wszLongSeed[c_nMaxSeedSize] = 0;
        pwszSeed = wszLongSeed;
        n = c_nMaxSeedSize;
    }
    p = (Byte *)pGuid;
    p[1] = n;
    *(UInt16 *)&p[2] = GenerateSeedCheckSum(pwszSeed, p[1]);
    nEncoder = 0;
    cPrevEncoded = 0;

    for (n = 0; n < c_cEncoders; n++) {
        memset(result, 0, 12);
        cEncoded = (*c_encoders[n].fnEncode)(
                        c_encoders[n].pvArg, pwszSeed, result);

        assert(cEncoded <= p[1] && "Guid Encoding Size Error");
        if (cEncoded > cPrevEncoded) {
            memcpy((Byte *)pGuid + 4, result, 12);
            nEncoder = n;
            cPrevEncoded = cEncoded;
        }
        if (cEncoded == p[1]) break;
    }
    p[0] = (Byte)(nEncoder << 5);
    p[0] |= (Byte)GenerateGuidCheckSum(*pGuid);

    return NOERROR;
}

ECode _DebugHelper_SeedStringFromGuid(REMuid guid, wchar_t *pwszSeed)
{
    Byte *p;
    Byte checkSum;

    p = (Byte *)&guid;
    checkSum = (Byte)GenerateGuidCheckSum(guid);
    if (checkSum != (p[0] & 0x1f)) return E_FAIL;

    (*c_encoders[p[0] >> 5].fnDecode)(
                c_encoders[p[0] >> 5].pvArg, (UInt32 *)&p[4], pwszSeed);
    if (p[1] < wcslen(pwszSeed)) pwszSeed[p[1]] = 0;
    return NOERROR;
}

ECode _DebugHelper_NameFromGuid(REMuid guid, wchar_t *pwszName)
{
    ECode ec;
    wchar_t *pSeed, wszSeed[_MAX_SEED_LEN + 1];

    ec = _DebugHelper_SeedStringFromGuid(guid, wszSeed);
    if (SUCCEEDED(ec)) {
        pSeed = wszSeed;
        while (0 != *pSeed && L'/' != *pSeed) {
            *pwszName++ = *pSeed++;
        }
        *pwszName = 0;
    }
    return ec;
}
